use std::{ptr::null_mut, sync::Arc, time::Instant};

use ffmpeg_sys_next::{
    av_frame_alloc, av_frame_free, av_packet_alloc, av_packet_unref, av_q2d,
    avcodec_alloc_context3, avcodec_open2, avcodec_parameters_to_context, avcodec_receive_frame,
    avcodec_send_packet, sws_freeContext, sws_getContext, sws_scale, AVCodec, AVCodecContext,
    AVCodecParameters, AVFrame, AVPixelFormat, AVRational, AVERROR, AVERROR_EOF, EAGAIN,
    SWS_BILINEAR,
};
use sdl2::{
    event::{Event, WindowEvent},
    keyboard::Keycode,
    pixels::PixelFormatEnum,
    rect::Rect,
    Sdl, VideoSubsystem,
};
use tokio::{
    sync::{
        mpsc::{channel, Receiver, Sender},
        Mutex, RwLock,
    },
    time::{sleep, Duration},
};

use crate::{
    CustomError, DecoderData, Packet, PlayStatus, FRAME_QUEUE_MAX_SIZE, MAX_STOP_SLEEP_TIME,
    PACKET_QUEUE_MAX_SIZE,
};

#[derive(Clone)]
pub struct VideoPlayer {
    pub width: Arc<Mutex<i32>>,
    pub height: Arc<Mutex<i32>>,
    pub original_width: i32,
    pub original_height: i32,
    pub time_base: AVRational,
    pub video_index: isize,
    pub pix_fmt: AVPixelFormat,
    pub video_codec_parameters: *mut AVCodecParameters,
    pub video_codec: *mut AVCodec,
    pub video_codec_context: *mut AVCodecContext,
    pub packet_queue_sender: Sender<Packet>,
    pub packet_queue_receiver: Arc<Mutex<Receiver<Packet>>>,
    pub frame_queue_sender: Sender<DecoderData>,
    pub frame_queue_receiver: Arc<Mutex<Receiver<DecoderData>>>,
    pub fps: i32,
    pub status: Arc<Mutex<PlayStatus>>,
    pub window_width: i32,
    pub window_height: i32,
    pub title: String,
    pub sdl_context: Sdl,
    pub video_subsystem: VideoSubsystem,
    pub seek_sender: Sender<i8>,
}

unsafe impl Send for VideoPlayer {}

impl VideoPlayer {
    pub fn new(
        status: Arc<Mutex<PlayStatus>>,
        title: String,
        sdl: Sdl,
        seek_sender: Sender<i8>,
    ) -> Self {
        let (frame_queue_sender, frame_queue_receiver): (
            Sender<DecoderData>,
            Receiver<DecoderData>,
        ) = channel(FRAME_QUEUE_MAX_SIZE);
        let (packet_queue_sender, packet_queue_receiver): (Sender<Packet>, Receiver<Packet>) =
            channel(PACKET_QUEUE_MAX_SIZE);
        let video_subsystem = sdl.video().unwrap();
        VideoPlayer {
            width: Arc::new(Mutex::new(0)),
            height: Arc::new(Mutex::new(0)),
            original_width: 0,
            original_height: 0,
            time_base: AVRational { num: 0, den: 0 },
            pix_fmt: AVPixelFormat::AV_PIX_FMT_NONE,
            video_index: -1,
            video_codec_parameters: null_mut(),
            video_codec: null_mut(),
            video_codec_context: null_mut(),
            packet_queue_sender,
            packet_queue_receiver: Arc::new(Mutex::new(packet_queue_receiver)),
            frame_queue_sender,
            frame_queue_receiver: Arc::new(Mutex::new(frame_queue_receiver)),
            fps: 0,
            status,
            window_width: 0,
            window_height: 0,
            title,
            sdl_context: sdl,
            video_subsystem,
            seek_sender,
        }
    }

    pub async fn init_window(&mut self) -> Result<(), CustomError> {
        if self.video_index == -1 {
            return Ok(());
        }
        self.window_width = self.original_width;
        self.window_height = self.original_height;

        if let Ok(rect) = self.video_subsystem.display_bounds(0) {
            let screen_width = rect.width() as i32;
            let screen_height = rect.height() as i32;
            let (mut width, mut height) = (self.original_width, self.original_height);
            let (video_width, video_height) = (self.original_width, self.original_height);
            if video_width > screen_width || video_height > screen_height {
                (width, height) = self.reset_window(screen_width, screen_height)?;
            }
            self.width = Arc::new(Mutex::new(width));
            self.height = Arc::new(Mutex::new(height));
        };
        Ok(())
    }

    /**
     * 复制编解码器上下文对象，用于保存从视频流中抽取的帧
     */
    pub async fn codec_alloc_context(&mut self) -> Result<(), CustomError> {
        if self.video_index == -1 {
            return Ok(());
        }
        let codec_context = unsafe { avcodec_alloc_context3(self.video_codec) };
        if codec_context.is_null() {
            return Err(CustomError::FfmepgError("Unsupported codec".to_owned()));
        }
        self.video_codec_context = codec_context;
        Ok(())
    }

    /**
     * 复制编解码器上下文对象
     */
    pub async fn parameters_to_context(&mut self) -> Result<(), CustomError> {
        if self.video_index == -1 {
            return Ok(());
        }
        if unsafe {
            avcodec_parameters_to_context(self.video_codec_context, self.video_codec_parameters)
        } != 0
        {
            return Err(CustomError::FfmepgError(
                "Couldn't copy codec context".to_owned(),
            ));
        }
        Ok(())
    }

    /**
     * 打开解码器
     */
    pub async fn open_codec(&mut self) -> Result<(), CustomError> {
        if self.video_index == -1 {
            return Ok(());
        }
        if unsafe { avcodec_open2(self.video_codec_context, self.video_codec, null_mut()) } != 0 {
            return Err(CustomError::FfmepgError("Could not open codec.".to_owned()));
        }
        self.pix_fmt = unsafe { (*self.video_codec_context).pix_fmt };
        Ok(())
    }

    pub async fn decode_packet(&mut self, has_audio: bool) -> Result<(), CustomError> {
        loop {
            {
                let status = self.status.lock().await;
                if let PlayStatus::Pause = *status {
                    sleep(Duration::from_millis(MAX_STOP_SLEEP_TIME)).await;
                    continue;
                };
            }

            let mut pkt: Packet = {
                let mut packet_queue_receiver_lock = self.packet_queue_receiver.lock().await;
                match packet_queue_receiver_lock.try_recv() {
                    Ok(packet) => packet,
                    Err(_) => {
                        let mut status_lock = self.status.lock().await;
                        match *status_lock {
                            PlayStatus::PacketEnd => {
                                if has_audio {
                                    *status_lock = PlayStatus::VideoFrameEnd;
                                } else {
                                    *status_lock = PlayStatus::End;
                                }
                                break;
                            }
                            PlayStatus::AudioFrameEnd => {
                                *status_lock = PlayStatus::End;
                                break;
                            }
                            _ => {
                                continue;
                            }
                        };
                    }
                }
            };

            let packet = unsafe { av_packet_alloc() };
            unsafe {
                pkt.to_av_packet(packet);
            }

            if unsafe { avcodec_send_packet(self.video_codec_context, packet) } < 0 {
                unsafe {
                    av_packet_unref(packet);
                }
                println!("Failed to decode packet.");
                return Err(CustomError::FfmepgError(
                    "Failed to decode packet".to_owned(),
                ));
            }
            unsafe {
                av_packet_unref(packet);
            }

            loop {
                let mut decoder_data = DecoderData::default();
                {
                    let (width, height);
                    {
                        let width_lock = self.width.lock().await;
                        let height_lock = self.height.lock().await;
                        width = *width_lock;
                        height = *height_lock;
                    }
                    let mut frame = unsafe { av_frame_alloc() };
                    let res = unsafe { avcodec_receive_frame(self.video_codec_context, frame) };
                    if res == 0 {
                        decoder_data
                            .data
                            .reserve_exact((width * height * 4) as usize);
                        let dest_linesize = [width * 3];
                        let dest = [decoder_data.data.as_mut_ptr()];
                        self.sws_scale(frame, dest_linesize, dest, width, height)?;
                        decoder_data.pts = unsafe { (*frame).pts };
                        decoder_data.time_base = self.time_base;
                        decoder_data.width = width;
                        decoder_data.height = height;

                        unsafe {
                            av_frame_free(&mut frame);
                        }
                    } else if res == AVERROR_EOF || res == AVERROR(EAGAIN) {
                        unsafe {
                            av_frame_free(&mut frame);
                        }
                        break;
                    } else {
                        unsafe {
                            av_frame_free(&mut frame);
                        }
                        println!("Failed decode, ");
                        return Err(CustomError::FfmepgError("Failed decode".to_lowercase()));
                    }
                }

                let _ = self.frame_queue_sender.send(decoder_data).await;
            }
        }
        Ok(())
    }

    pub fn sws_scale(
        &self,
        frame: *mut AVFrame,
        dest_linesize: [i32; 1],
        dest: [*mut u8; 1],
        width: i32,
        height: i32,
    ) -> Result<(), CustomError> {
        let sws_context = unsafe {
            sws_getContext(
                self.original_width,
                self.original_height,
                self.pix_fmt,
                width,
                height,
                AVPixelFormat::AV_PIX_FMT_RGB24,
                SWS_BILINEAR,
                null_mut(),
                null_mut(),
                null_mut(),
            )
        };
        let res = unsafe {
            sws_scale(
                sws_context,
                (*frame).data.as_ptr() as *const *const u8,
                (*frame).linesize.as_ptr(),
                0,
                (*frame).height,
                dest.as_ptr(),
                dest_linesize.as_ptr(),
            )
        };
        unsafe {
            sws_freeContext(sws_context);
        }
        if res < 0 {
            return Err(CustomError::FfmepgError(
                "视频像素格式和分辨率的转换失败.".to_owned(),
            ));
        }
        Ok(())
    }

    pub async fn video_player(&mut self, audio_time: Arc<RwLock<f64>>) -> Result<(), CustomError> {
        if self.video_index == -1 {
            return Ok(());
        }
        let (width, height);
        {
            let width_lock = self.width.lock().await;
            let height_lock = self.height.lock().await;
            width = *width_lock;
            height = *height_lock;
        }

        let window = self
            .video_subsystem
            .window(&self.title, width as u32, height as u32)
            .resizable()
            .position_centered()
            .opengl()
            .build()
            .map_err(|e| e.to_string())
            .unwrap();

        let mut canvas = window
            .into_canvas()
            .build()
            .map_err(|e| e.to_string())
            .unwrap();
        let texture_creator = canvas.texture_creator();
        let mut texture;

        let mut event_pump = self.sdl_context.event_pump().unwrap();

        let mut curr_status: PlayStatus = PlayStatus::Start;
        'running: loop {
            let now = Instant::now();
            for event in event_pump.poll_iter() {
                match event {
                    Event::Quit { .. }
                    | Event::KeyDown {
                        keycode: Some(Keycode::Escape),
                        ..
                    } => {
                        {
                            let mut play_status_lock = self.status.lock().await;
                            *play_status_lock = PlayStatus::End;
                        }
                        break 'running;
                    }
                    Event::KeyDown {
                        keycode: Some(Keycode::Space),
                        ..
                    } => {
                        let mut play_status_lock = self.status.lock().await;
                        let status = match *play_status_lock {
                            PlayStatus::Pause => PlayStatus::Start,
                            PlayStatus::PacketEnd => {
                                curr_status = PlayStatus::PacketEnd;
                                PlayStatus::Pause
                            }
                            PlayStatus::Start => {
                                curr_status = PlayStatus::Start;
                                PlayStatus::Pause
                            }
                            PlayStatus::Resume => {
                                curr_status = PlayStatus::Resume;
                                PlayStatus::Pause
                            }
                            PlayStatus::VideoFrameEnd => {
                                curr_status = PlayStatus::VideoFrameEnd;
                                PlayStatus::Pause
                            }
                            PlayStatus::AudioFrameEnd => {
                                curr_status = PlayStatus::AudioFrameEnd;
                                PlayStatus::Pause
                            }
                            PlayStatus::End => {
                                curr_status = PlayStatus::End;
                                PlayStatus::Pause
                            }
                        };
                        //println!("{:?}", status);
                        if let PlayStatus::Pause = status {
                            *play_status_lock = status;
                        } else {
                            *play_status_lock = curr_status.clone();
                        };
                    }
                    Event::KeyDown {
                        timestamp: _,
                        window_id: _,
                        keycode,
                        scancode: _,
                        keymod: _,
                        repeat: _,
                    } => {
                        match keycode {
                            Some(Keycode::Right) => {
                                println!("快进");
                                let _ = self.seek_sender.send(5).await;
                            }
                            Some(Keycode::Left) => {
                                println!("快退");
                                let _ = self.seek_sender.send(-15).await;
                            }
                            _ => {}
                        };
                    }
                    Event::Window {
                        timestamp: _,
                        window_id: _,
                        win_event,
                    } => {
                        if let WindowEvent::Resized(window_width, window_height) = win_event {
                            {
                                let mut width_lock = self.width.lock().await;
                                let mut height_lock = self.height.lock().await;
                                let (width, height) =
                                    self.reset_window(window_width, window_height)?;
                                *width_lock = width;
                                *height_lock = height;
                            }
                            self.window_width = window_width;
                            self.window_height = window_height;
                        }
                    }
                    _ => {}
                }
            }

            let play_end = {
                let status = self.status.lock().await;
                match *status {
                    PlayStatus::Pause => {
                        sleep(Duration::from_millis(MAX_STOP_SLEEP_TIME)).await;
                        continue;
                    }
                    PlayStatus::VideoFrameEnd => PlayStatus::VideoFrameEnd,
                    PlayStatus::End => PlayStatus::End,
                    _ => PlayStatus::Start,
                }
            };

            match play_end {
                PlayStatus::End => {
                    println!("Play end");
                    break 'running;
                }
                PlayStatus::VideoFrameEnd => {
                    //println!("video frame end continue");
                    sleep(Duration::from_millis(MAX_STOP_SLEEP_TIME)).await;
                    continue;
                }
                _ => {
                    //println!("video player {:?}", play_end);
                    let (frame_decoder_data, time_base, pts, data_width, data_height) = {
                        let mut frame_queue_receiver_lock = self.frame_queue_receiver.lock().await;
                        match frame_queue_receiver_lock.recv().await {
                            Some(decoder_data) => (
                                decoder_data.data,
                                decoder_data.time_base,
                                decoder_data.pts,
                                decoder_data.width,
                                decoder_data.height,
                            ),
                            None => {
                                println!("frame queue no data. ");
                                sleep(Duration::from_millis(1)).await;
                                continue;
                            }
                        }
                    };
                    //println!("video get");
                    texture = texture_creator
                        .create_texture_target(
                            PixelFormatEnum::RGB24,
                            data_width as u32,
                            data_height as u32,
                        )
                        .unwrap();
                    texture
                        .update(
                            None,
                            frame_decoder_data.as_slice(),
                            (data_width * 3) as usize,
                        )
                        .expect("update texture error");
                    canvas.clear();
                    canvas
                        .copy(
                            &texture,
                            None,
                            Some(Rect::new(
                                (self.window_width - data_width) / 2,
                                (self.window_height - data_height) / 2,
                                data_width as u32,
                                data_height as u32,
                            )),
                        )
                        .unwrap();

                    canvas.present();
                    //sleep(Duration::from_millis(40)).await;
                    // println!("{}", self.fps);

                    let video_time = unsafe { pts as f64 * av_q2d(time_base) };
                    let distance = match self.fps {
                        0 => -(now.elapsed().as_millis() as i64),
                        _ => (1000 / self.fps) as i64 - now.elapsed().as_millis() as i64,
                    };
                    //println!("fps: {}, {} {}", self.fps, distance, now.elapsed().as_millis() );
                    self.async_audio(video_time, distance, audio_time.clone())
                        .await;
                }
            };
        }
        Ok(())
    }

    pub fn reset_window(
        &self,
        window_width: i32,
        window_height: i32,
    ) -> Result<(i32, i32), CustomError> {
        let (width, height);
        let video_width = self.original_width;
        let video_height = self.original_height;
        let width_ratio = video_width as f64 / window_width as f64;
        let height_ratio = video_height as f64 / window_height as f64;
        if width_ratio >= height_ratio {
            (width, height) = (window_width, (window_width * video_height / video_width));
        } else {
            (width, height) = ((window_height * video_width / video_height), window_height);
        }
        Ok((width, height))
    }

    /**
       同步音频
    */
    async fn async_audio(&self, video_time: f64, distance: i64, audio_time: Arc<RwLock<f64>>) {
        loop {
            //println!("audio sleep distance: {}", distance);
            let mut d_time = 0.0;
            {
                let audio_sec = audio_time.read().await;
                if *audio_sec > 0.0 {
                    d_time = video_time - *audio_sec;
                }
            }

            let distance_total = distance + (d_time * 1000.0) as i64;
            if distance_total <= 0 {
                break;
            }

            //println!("async audio sleep distance: {} video_time:{}, audio_sec:{}", distance_total, video_time, audio_s);
            if distance_total > 0 && distance_total <= 100 {
                sleep(Duration::from_millis(distance_total as u64)).await;
                break;
            } else if distance_total > 100 {
                sleep(Duration::from_millis(100)).await;
            }
        }
    }
}
